// =============================================================
// Coord
// ---------------------------------------
// Author : Woo-Hyun Kim
// Email : woorung@icu.ac.kr or woorung@nhncorp.com
// Date : Oct. 8, 2007
// =============================================================
#include <iostream>

#include <sys/timeb.h>
#include <time.h>

#include "common_coord.h"
#include "property/CProperty.h"

#include "thread/CThread.h"
#include "service/CServiceRepository.h"

#include "CKeepAliveThread.h"

#include "CDiscoveryService.h"
#include "CDiscoveryProxy.h"

#include "md5/CMD5.h"

// available services
#include "CSpaceService.h"

void randomize(int seed)
{
        srand(seed);
}

int new_random()
{
        return rand();
}

// coord discovery server
void coord_sd(int port)
{
        CServiceRepository *service_container = NULL;
        service_container = new CServiceRepository(port);

        // discovery
        CDiscoveryService discovery_service(CUBE_SERVICE_DISCOVERY);
        service_container->plugin(&discovery_service);

        cout << "===================================================" << endl;
        cout << COORD_VER << " : coord cluster master" << endl;
        cout << endl;
        cout << "# available services" << endl;
        cout << discovery_service.getName() << endl;
        cout << "===================================================" << endl;

        service_container->run();

        delete service_container;
}

// coord discovery server
void coord_sd(char *conf)
{
        CServiceRepository *service_container = NULL;
        service_container = new CServiceRepository(conf);

        // discovery
        CDiscoveryService discovery_service(CUBE_SERVICE_DISCOVERY);
        service_container->plugin(&discovery_service);

        cout << "===================================================" << endl;
        cout << COORD_VER << " : coord cluster master" << endl;
        cout << endl;
        cout << "# available services" << endl;
        cout << discovery_service.getName() << endl;
        cout << "===================================================" << endl;

        service_container->run();

        delete service_container;
}

// coord itself
void coord_sp(char *conf)
{
	CProperty config(conf);
        pair<string,string> kv;
        kv.first = COORD_CONF_MASTER_IP_KEY;
        string host = config.get(kv).second;
        kv.first = COORD_CONF_MASTER_PORT_KEY;
        int port = atoi((char *)config.get(kv).second.c_str());

        CServiceRepository *service_container = NULL;
        service_container = new CServiceRepository(conf);

        // space service
        CSpaceService space_service(CUBE_SERVICE_SPACE);
        service_container->plugin(&space_service);

        cout << "===================================================" << endl;
        cout << COORD_VER << " : coord cluster worker" << endl;
        cout << "run on port = " << service_container->getPort() << endl;
        cout << endl;
        cout << "# available services" << endl;
        cout << space_service.getName() << endl;
        cout << "===================================================" << endl;

        // run keep-alive thread
        char thr_name[50];
        sprintf(thr_name,"coord.cluster.worker-%d",new_random()%5000);
        CKeepAliveThread keepalive(thr_name);
        keepalive.bind((char *)host.c_str(), port, service_container);
        keepalive.keepalive((new_random()%15)+5);
        keepalive.start();

        // run coord itself
        service_container->run();

        delete service_container;
}

// coord itself
void coord_sp(char *host, int port)
{
        CServiceRepository *service_container = NULL;

        randomize(getpid());

        // set random port
        int random_port = (new_random()%1000)+10080;
        service_container = new CServiceRepository(random_port);

        // space service
        CSpaceService space_service(CUBE_SERVICE_SPACE);
        service_container->plugin(&space_service);

        cout << "===================================================" << endl;
        cout << COORD_VER << " : coord cluster worker" << endl;
        cout << "run on port = " << random_port << endl;
        cout << endl;
        cout << "# available services" << endl;
        cout << space_service.getName() << endl;
        cout << "===================================================" << endl;

        // run keep-alive thread
        char thr_name[50];
        sprintf(thr_name,"coord.cluster.worker-%d",new_random()%5000);
        CKeepAliveThread keepalive(thr_name);
        keepalive.bind(host, port, service_container);
        keepalive.keepalive((new_random()%15)+5);
        keepalive.start();

        // run coord itself
        service_container->run();

        delete service_container;
}

void print_usage(char *prog)
{
	cout << "-------------------------------------------------------------------------" << endl;
	cout << "usage : " << prog << " [-c <config> ] [-p <port> -h <ip>] [-n <num>]" << endl;
	cout << "\t-c\t\tconfiguration file" << endl;
	cout << "\t-p\t\tport" << endl;
	cout << "\t-h\t\tip to connect" << endl;
	cout << "\t-n\t\tcoord cluster workers" << endl;
	cout << endl;
	cout << "example : " << endl;
	cout << "-- coord cluster master" << endl;
	cout << "\t" << prog << " -p 18088" << endl;
	cout << "\t" << prog << " -c master.conf" << endl;
	cout << endl;
	cout << "-- coord cluster worker" << endl;
	cout << "\t" << prog << " -h localhost -p 18088 -n 2" << endl;
	cout << "\t" << prog << " -c worker.conf -n 2" << endl;
	cout << "-------------------------------------------------------------------------" << endl;
}

int main(int argc, char **argv)
{
	if(argc<2) {
		print_usage(argv[0]);
		exit(-1);
	}

	int opt;
	static char optstr[] = "cphn";

	char *conf= NULL;
	int port = -1;
	int num = 1;
	string host = "";
	
	while((opt=getopt(argc,argv,optstr))!=-1) {
		switch(opt) {
			case 'c' :
			{
				conf = argv[optind++];
				break;
			}
			case 'p' :
				port = atoi(argv[optind++]);
				break;
			case 'h' :
				host = argv[optind++];
				break;
			case 'n' :
				num = atoi(argv[optind++]);
				break;
			default :
				print_usage(argv[0]);
				exit(-1);
		}
	}

	if(port>0&&host=="") coord_sd(port);
	else if(conf!=NULL) {
		CProperty config(conf);
		
		pair<string,string> kv;
		kv.first = COORD_CONF_CUBE_ROLE_KEY;
		string coord_role = config.get(kv).second;

		if(coord_role!="") {
			if(coord_role==COORD_CONF_MASTER_ROLE) coord_sd(conf);
			else {
				vector<pid_t> pids;
				for(int i=0;i<num;i++) {
					pid_t pid = fork();
					if(pid==0) { // child
						coord_sp(conf);
						exit(1);
					}
					else if(pid>0) pids.push_back(pid); // parent
					else { // fork error
						sleep(1);
						i--;
					}
				}

				for(int i=0;i<num;i++) {
					waitpid(pids[i],NULL,0);
				}
			}
		}
		else {
			cerr << "[config error]there is no " << COORD_CONF_CUBE_ROLE_KEY << endl;
			exit(-1);
		}
	}
	else {
		vector<pid_t> pids;
		for(int i=0;i<num;i++) {
			pid_t pid = fork();
			if(pid==0) { // child
				coord_sp((char *)host.c_str(), port);
				exit(1);
			}
			else if(pid>0) pids.push_back(pid); // parent
			else { // fork error
				sleep(1);
				i--;
			}
		}

		for(int i=0;i<num;i++) {
			waitpid(pids[i],NULL,0);
		}
	}

        return 1;
}
